// Copyright 2023 Specter Ops, Inc.
// 
// Licensed under the Apache License, Version 2.0
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
//     http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// 
// SPDX-License-Identifier: Apache-2.0

package v2

import (
	"fmt"
	"net/http"

	"github.com/specterops/bloodhound/src/api"
	adAnalysis "github.com/specterops/bloodhound/analysis/ad"
	"github.com/specterops/bloodhound/dawgs/graph"
	"github.com/specterops/bloodhound/graphschema/ad"
)

var computerQueries = map[string]any{
	"sessions":         adAnalysis.FetchComputerSessions,
	"adminUsers":       adAnalysis.CreateInboundLocalGroupListDelegate(ad.AdminTo),
	"rdpUsers":         adAnalysis.CreateInboundLocalGroupListDelegate(ad.CanRDP),
	"dcomUsers":        adAnalysis.CreateInboundLocalGroupListDelegate(ad.ExecuteDCOM),
	"psRemoteUsers":    adAnalysis.CreateInboundLocalGroupListDelegate(ad.CanPSRemote),
	"sqlAdminUsers":    adAnalysis.CreateSQLAdminListDelegate(graph.DirectionInbound),
	"constrainedUsers": adAnalysis.CreateConstrainedDelegationListDelegate(graph.DirectionInbound),
	"groupMembership":  adAnalysis.FetchEntityGroupMembership,
	"adminRights":      adAnalysis.CreateOutboundLocalGroupListDelegate(ad.AdminTo),
	"rdpRights":        adAnalysis.CreateOutboundLocalGroupListDelegate(ad.CanRDP),
	"dcomRights":       adAnalysis.CreateOutboundLocalGroupListDelegate(ad.ExecuteDCOM),
	"psRemoteRights":   adAnalysis.CreateOutboundLocalGroupListDelegate(ad.CanPSRemote),
	"constrainedPrivs": adAnalysis.CreateConstrainedDelegationListDelegate(graph.DirectionOutbound),
	"controllables":    adAnalysis.FetchOutboundADEntityControl,
	"controllers":      adAnalysis.FetchInboundADEntityControllers,
	"gpos":             adAnalysis.FetchEnforcedGPOs,
}

func (s *Resources) GetComputerEntityInfo(response http.ResponseWriter, request *http.Request) {
	if hydrateCounts, err := api.ParseOptionalBool(request.URL.Query().Get(api.QueryParameterHydrateCounts), true); err != nil {
		api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusBadRequest, api.ErrorResponseDetailsBadQueryParameterFilters, request), response)
	} else if objectId, err := GetEntityObjectIDFromRequestPath(request); err != nil {
		api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusBadRequest, fmt.Sprintf("error reading objectid: %v", err), request), response)
	} else if node, err := s.GraphQuery.GetEntityByObjectId(request.Context(), objectId, ad.Computer); err != nil {
		if graph.IsErrNotFound(err) {
			api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusNotFound, "node not found", request), response)
		} else {
			api.WriteErrorResponse(request.Context(), api.BuildErrorResponse(http.StatusInternalServerError, fmt.Sprintf("error getting node: %v", err), request), response)
		}
	} else if hydrateCounts {
		results := s.GraphQuery.GetEntityCountResults(request.Context(), node, computerQueries)
		api.WriteBasicResponse(request.Context(), results, http.StatusOK, response)
	} else {
		results := map[string]any{"props": node.Properties.Map}
		api.WriteBasicResponse(request.Context(), results, http.StatusOK, response)
	}
}
